
module iTimer.av.Mpg123;

import iTimer.Types;

alias int c_int ;
alias int c_long ;

alias uint c_uint ;
alias uint c_ulong ;
alias double c_double;
alias ptrdiff_t c_off_t ;

extern(Windows) {
    const WAVE_FORMAT_PCM=1;
    const UINT WAVE_MAPPER= -1;  // FIXME: This doesn't make sense!
    const CALLBACK_TYPEMASK = 0x70000;
    const CALLBACK_NULL     = 0;
    const CALLBACK_WINDOW   = 0x10000;
    const CALLBACK_TASK     = 0x20000;
    const CALLBACK_FUNCTION = 0x30000;
    const CALLBACK_THREAD   = CALLBACK_TASK;
    const CALLBACK_EVENT    = 0x50000;

    typedef HANDLE HWAVE;
    typedef HANDLE HWAVEIN;
    typedef HANDLE HWAVEOUT;
    typedef HANDLE HDRVR;
    alias HWAVEIN* LPHWAVEIN;
    alias HWAVEOUT* LPHWAVEOUT;
    // alias UINT MMRESULT;
    alias uint*     LPDWORD, LPUINT;    // alias uint*       PULONG, PDWORD, LPDWORD, PUINT, LPUINT;

    const TIME_MS      = 1;
    const TIME_SAMPLES = 2;
    const TIME_BYTES   = 4;
    const TIME_SMPTE   = 8;
    const TIME_MIDI    = 16;
    const TIME_TICKS   = 32;

    const MMSYSERR_BASE     =    0;
    const WAVERR_BASE       =   32;
    const MIDIERR_BASE      =   64;
    const TIMERR_BASE       =   96;
    const JOYERR_BASE       =  160;
    const MCIERR_BASE       =  256;
    const MIXERR_BASE       = 1024;
    const MCI_STRING_OFFSET =  512;
    const MCI_VD_OFFSET     = 1024;
    const MCI_CD_OFFSET     = 1088;
    const MCI_WAVE_OFFSET   = 1152;
    const MCI_SEQ_OFFSET    = 1216;
    
    enum {
        MMSYSERR_NOERROR        = 0,
        MMSYSERR_ERROR          = MMSYSERR_BASE+1,
        MMSYSERR_BADDEVICEID, // 2
        MMSYSERR_NOTENABLED, // 3
        MMSYSERR_ALLOCATED, // 4
        MMSYSERR_INVALHANDLE, // 5
        MMSYSERR_NODRIVER, // 6
        MMSYSERR_NOMEM, //7
        MMSYSERR_NOTSUPPORTED, //8 
        MMSYSERR_BADERRNUM, //9 
        MMSYSERR_INVALFLAG, // 10
        MMSYSERR_INVALPARAM, //11
        MMSYSERR_HANDLEBUSY, //12
        MMSYSERR_INVALIDALIAS, //13
        MMSYSERR_BADDB, // 14
        MMSYSERR_KEYNOTFOUND, // 15
        MMSYSERR_READERROR, // 16
        MMSYSERR_WRITEERROR, //17
        MMSYSERR_DELETEERROR, //18
        MMSYSERR_VALNOTFOUND, //19
        MMSYSERR_NODRIVERCB, // = MMSYSERR_BASE+20
        MMSYSERR_LASTERROR      = MMSYSERR_NODRIVERCB // 20
    }
    enum {
        MM_JOY1MOVE            = 0x3A0,
        MM_JOY2MOVE,
        MM_JOY1ZMOVE,
        MM_JOY2ZMOVE,       // = 0x3A3
        MM_JOY1BUTTONDOWN      = 0x3B5,
        MM_JOY2BUTTONDOWN,
        MM_JOY1BUTTONUP,
        MM_JOY2BUTTONUP,
        MM_MCINOTIFY,       // = 0x3B9
        MM_WOM_OPEN            = 0x3BB,
        MM_WOM_CLOSE,
        MM_WOM_DONE,
        MM_WIM_OPEN,
        MM_WIM_CLOSE,
        MM_WIM_DATA,
        MM_MIM_OPEN,
        MM_MIM_CLOSE,
        MM_MIM_DATA,
        MM_MIM_LONGDATA,
        MM_MIM_ERROR,
        MM_MIM_LONGERROR,
        MM_MOM_OPEN,
        MM_MOM_CLOSE,
        MM_MOM_DONE,        // = 0x3C9
        MM_DRVM_OPEN           = 0x3D0,
        MM_DRVM_CLOSE,
        MM_DRVM_DATA,
        MM_DRVM_ERROR,
        MM_STREAM_OPEN,
        MM_STREAM_CLOSE,
        MM_STREAM_DONE,
        MM_STREAM_ERROR,    // = 0x3D7
        MM_MOM_POSITIONCB      = 0x3CA,
        MM_MCISIGNAL,
        MM_MIM_MOREDATA,    // = 0x3CC
        MM_MIXM_LINE_CHANGE    = 0x3D0,
        MM_MIXM_CONTROL_CHANGE = 0x3D1
    }
    const WOM_OPEN  = MM_WOM_OPEN;
    const WOM_CLOSE = MM_WOM_CLOSE;
    const WOM_DONE  = MM_WOM_DONE;
    const WIM_OPEN  = MM_WIM_OPEN;
    const WIM_CLOSE = MM_WIM_CLOSE;
    const WIM_DATA  = MM_WIM_DATA;

    align(1):
    struct WAVEHDR {
        LPSTR lpData;
        DWORD dwBufferLength;
        DWORD dwBytesRecorded;
        DWORD dwUser;
        DWORD dwFlags;
        DWORD dwLoops;
        WAVEHDR *lpNext;
        DWORD reserved;
    }
    alias WAVEHDR* PWAVEHDR, LPWAVEHDR;
    /*
    struct WAVEFORMATEX {
        WORD wFormatTag;
        WORD nChannels;
        DWORD nSamplesPerSec;
        DWORD nAvgBytesPerSec;
        WORD nBlockAlign;
        WORD wBitsPerSample;
        WORD cbSize;
    }
    */
    alias WAVEFORMATEX* PWAVEFORMATEX, LPWAVEFORMATEX;
    alias WAVEFORMATEX* LPCWAVEFORMATEX;
    
    struct MMTIME {
        UINT wType;
        union {
            DWORD ms;
            DWORD sample;
            DWORD cb;
            DWORD ticks;
            struct smpte {
                BYTE hour;
                BYTE min;
                BYTE sec;
                BYTE frame;
                BYTE fps;
                BYTE dummy;
                BYTE pad[2];
            };
            struct midi {
                DWORD songptrpos;
            }
        }
    }
    alias MMTIME* PMMTIME, LPMMTIME;
    struct TIMECAPS {
        UINT wPeriodMin;
        UINT wPeriodMax;
    }
    alias TIMECAPS* PTIMECAPS, LPTIMECAPS;
    alias void function (UINT,UINT,DWORD,DWORD,DWORD) LPTIMECALLBACK;

    LRESULT CloseDriver(HDRVR,LONG,LONG);
    HDRVR OpenDriver(LPCWSTR,LPCWSTR,LONG);
    LRESULT SendDriverMessage(HDRVR,UINT,LONG,LONG);
    HMODULE DrvGetModuleHandle(HDRVR);
    HMODULE GetDriverModuleHandle(HDRVR);
    LRESULT DefDriverProc(DWORD,HDRVR,UINT,LPARAM,LPARAM);
    UINT mmsystemGetVersion();
    // FIXME: I believe this next line is a mistake
    //alias OutputDebugString OutputDebugStr;
    BOOL sndPlaySoundA(LPCSTR,UINT);
    BOOL sndPlaySoundW(LPCWSTR,UINT);
    BOOL PlaySoundA(LPCSTR,HMODULE,DWORD);
    BOOL PlaySoundW(LPCWSTR,HMODULE,DWORD);
    UINT waveOutGetNumDevs();
    MMRESULT waveOutGetVolume(HWAVEOUT,PDWORD);
    MMRESULT waveOutSetVolume(HWAVEOUT,DWORD);
    MMRESULT waveOutGetErrorTextA(MMRESULT,LPSTR,UINT);
    MMRESULT waveOutGetErrorTextW(MMRESULT,LPWSTR,UINT);
    MMRESULT waveOutOpen(LPHWAVEOUT,UINT,LPCWAVEFORMATEX,DWORD,DWORD,DWORD);
    MMRESULT waveOutClose(HWAVEOUT);
    MMRESULT waveOutPrepareHeader(HWAVEOUT,LPWAVEHDR,UINT);
    MMRESULT waveOutUnprepareHeader(HWAVEOUT,LPWAVEHDR,UINT);
    MMRESULT waveOutWrite(HWAVEOUT,LPWAVEHDR,UINT);
    MMRESULT waveOutPause(HWAVEOUT);
    MMRESULT waveOutRestart(HWAVEOUT);
    MMRESULT waveOutReset(HWAVEOUT);
    MMRESULT waveOutBreakLoop(HWAVEOUT);
    MMRESULT waveOutGetPosition(HWAVEOUT,LPMMTIME,UINT);
    MMRESULT waveOutGetPitch(HWAVEOUT,PDWORD);
    MMRESULT waveOutSetPitch(HWAVEOUT,DWORD);
    MMRESULT waveOutGetPlaybackRate(HWAVEOUT,PDWORD);
    MMRESULT waveOutSetPlaybackRate(HWAVEOUT,DWORD);
    MMRESULT waveOutGetID(HWAVEOUT,LPUINT);
    MMRESULT waveOutMessage(HWAVEOUT,UINT,DWORD,DWORD);
    MMRESULT timeGetSystemTime(LPMMTIME,UINT);
    DWORD timeGetTime();
    MMRESULT timeSetEvent(UINT,UINT,LPTIMECALLBACK,DWORD,UINT);
    MMRESULT timeKillEvent(UINT);
    MMRESULT timeGetDevCaps(LPTIMECAPS,UINT);
    MMRESULT timeBeginPeriod(UINT);
    MMRESULT timeEndPeriod(UINT);
}

extern(C) :

alias void* mpg123_handle;
enum /* mpg123_errors */
{
    MPG123_DONE=-12,	/**< Message: Track ended. Stop decoding. */
    MPG123_NEW_FORMAT=-11,	/**< Message: Output format will be different on next call. Note that some libmpg123 versions between 1.4.3 and 1.8.0 insist on you calling mpg123_getformat() after getting this message code. Newer verisons behave like advertised: You have the chance to call mpg123_getformat(), but you can also just continue decoding and get your data. */
    MPG123_NEED_MORE=-10,	/**< Message: For feed reader: "Feed me more!" (call mpg123_feed() or mpg123_decode() with some new input data). */
    MPG123_ERR=-1,			/**< Generic Error */
    MPG123_OK=0, 			/**< Success */
    MPG123_BAD_OUTFORMAT, 	/**< Unable to set up output format! */
    MPG123_BAD_CHANNEL,		/**< Invalid channel number specified. */
    MPG123_BAD_RATE,		/**< Invalid sample rate specified.  */
    MPG123_ERR_16TO8TABLE,	/**< Unable to allocate memory for 16 to 8 converter table! */
    MPG123_BAD_PARAM,		/**< Bad parameter id! */
    MPG123_BAD_BUFFER,		/**< Bad buffer given -- invalid poc_inter or too small size. */
    MPG123_OUT_OF_MEM,		/**< Out of memory -- some malloc() failed. */
    MPG123_NOT_INITIALIZED,	/**< You didn't initialize the library! */
    MPG123_BAD_DECODER,		/**< Invalid decoder choice. */
    MPG123_BAD_HANDLE,		/**< Invalid mpg123 handle. */
    MPG123_NO_BUFFERS,		/**< Unable to initialize frame buffers (out of memory?). */
    MPG123_BAD_RVA,			/**< Invalid RVA mode. */
    MPG123_NO_GAPLESS,		/**< This build doesn't support gapless decoding. */
    MPG123_NO_SPACE,		/**< Not enough buffer space. */
    MPG123_BAD_TYPES,		/**< Incompatible numeric data types. */
    MPG123_BAD_BAND,		/**< Bad equalizer band. */
    MPG123_ERR_NULL,		/**< Null poc_inter given where valid storage address needed. */
    MPG123_ERR_READER,		/**< Error reading the stream. */
    MPG123_NO_SEEK_FROM_END,/**< Cannot seek from end (end is not known). */
    MPG123_BAD_WHENCE,		/**< Invalid 'whence' for seek function.*/
    MPG123_NO_TIMEOUT,		/**< Build does not support stream timeouts. */
    MPG123_BAD_FILE,		/**< File access error. */
    MPG123_NO_SEEK,			/**< Seek not supported by stream. */
    MPG123_NO_READER,		/**< No stream opened. */
    MPG123_BAD_PARS,		/**< Bad parameter handle. */
    MPG123_BAD_INDEX_PAR,	/**< Bad parameters to mpg123_index() and mpg123_set_index() */
    MPG123_OUT_OF_SYNC,	/**< Lost track in bytestream and did not try to resync. */
    MPG123_RESYNC_FAIL,	/**< Resync failed to find valid MPEG data. */
    MPG123_NO_8BIT,	/**< No 8bit encoding possible. */
    MPG123_BAD_ALIGN,	/**< Stack aligmnent error */
    MPG123_NULL_BUFFER,	/**< NULL input buffer with non-zero size... */
    MPG123_NO_RELSEEK,	/**< Relative seek not possible (screwed up file offset) */
    MPG123_NULL_POINTER, /**< You gave a null poc_inter somewhere where you shouldn't have. */
    MPG123_BAD_KEY,	/**< Bad key value given. */
    MPG123_NO_INDEX,	/**< No frame index in this build. */
    MPG123_INDEX_FAIL,	/**< Something with frame index went wrong. */
    MPG123_BAD_DECODER_SETUP,	/**< Something prevents a proper decoder setup */
    MPG123_MISSING_FEATURE  /**< This feature has not been built c_into libmpg123. */
    ,MPG123_BAD_VALUE /**< A bad value has been given, somewhere. */
    ,MPG123_LSEEK_FAILED /**< Low-level seek failed. */
    ,MPG123_BAD_CUSTOM_IO /**< Custom I/O not prepared. */
    ,MPG123_LFS_OVERFLOW /**< Offset value overflow during translation of large file API calls -- your client program cannot handle that large file. */
};
enum /*mpg123_parms*/
{
    MPG123_VERBOSE,        /**< set verbosity value for enabling messages to stderr, >= 0 makes sense (c_integer) */
    MPG123_FLAGS,          /**< set all flags, p.ex val = MPG123_GAPLESS|MPG123_MONO_MIX (c_integer) */
    MPG123_ADD_FLAGS,      /**< add some flags (c_integer) */
    MPG123_FORCE_RATE,     /**< when value > 0, force output rate to that value (c_integer) */
    MPG123_DOWN_SAMPLE,    /**< 0=native rate, 1=half rate, 2=quarter rate (c_integer) */
    MPG123_RVA,            /**< one of the RVA choices above (c_integer) */
    MPG123_DOWNSPEED,      /**< play a frame N times (c_integer) */
    MPG123_UPSPEED,        /**< play every Nth frame (c_integer) */
    MPG123_START_FRAME,    /**< start with this frame (skip frames before that, c_integer) */ 
    MPG123_DECODE_FRAMES,  /**< decode only this number of frames (c_integer) */
    MPG123_ICY_INTERVAL,   /**< stream contains ICY metadata with this c_interval (c_integer) */
    MPG123_OUTSCALE,       /**< the scale for output samples (amplitude - c_integer or float according to mpg123 output format, normally c_integer) */
    MPG123_TIMEOUT,        /**< timeout for reading from a stream (not supported on win32, c_integer) */
    MPG123_REMOVE_FLAGS,   /**< remove some flags (inverse of MPG123_ADD_FLAGS, c_integer) */
    MPG123_RESYNC_LIMIT,   /**< Try resync on frame parsing for that many bytes or until end of stream (<0 ... c_integer). */
    MPG123_INDEX_SIZE      /**< Set the frame index size (if supported). Values <0 mean that the index is allowed to grow dynamically in these steps (in positive direction, of course) -- Use this when you really want a full index with every individual frame. */
    ,MPG123_PREFRAMES /**< Decode/ignore that many frames in advance for layer 3. This is needed to fill bit reservoir after seeking, for example (but also at least one frame in advance is needed to have all "normal" data for layer 3). Give a positive c_integer value, please.*/
};
enum /*mpg123_param_flags*/
{
     MPG123_FORCE_MONO   = 0x7  /**<     0111 Force some mono mode: This is a test bitmask for seeing if any mono forcing is active. */
    ,MPG123_MONO_LEFT    = 0x1  /**<     0001 Force playback of left channel only.  */
    ,MPG123_MONO_RIGHT   = 0x2  /**<     0010 Force playback of right channel only. */
    ,MPG123_MONO_MIX     = 0x4  /**<     0100 Force playback of mixed mono.         */
    ,MPG123_FORCE_STEREO = 0x8  /**<     1000 Force stereo output.                  */
    ,MPG123_FORCE_8BIT   = 0x10 /**< 00010000 Force 8bit formats.                   */
    ,MPG123_QUIET        = 0x20 /**< 00100000 Suppress any prc_intouts (overrules verbose).                    */
    ,MPG123_GAPLESS      = 0x40 /**< 01000000 Enable gapless decoding (default on if libmpg123 has support). */
    ,MPG123_NO_RESYNC    = 0x80 /**< 10000000 Disable resync stream after error.                             */
    ,MPG123_SEEKBUFFER   = 0x100 /**< 000100000000 Enable small buffer on non-seekable streams to allow some peek-ahead (for better MPEG sync). */
    ,MPG123_FUZZY        = 0x200 /**< 001000000000 Enable fuzzy seeks (guessing byte offsets or using approximate seek poc_ints from Xing TOC) */
    ,MPG123_FORCE_FLOAT  = 0x400 /**< 010000000000 Force floating poc_int output (32 or 64 bits depends on mpg123 c_internal precision). */
    ,MPG123_PLAIN_ID3TEXT = 0x800 /**< 100000000000 Do not translate ID3 text data to UTF-8. ID3 strings will contain the raw text data, with the first byte containing the ID3 encoding code. */
    ,MPG123_IGNORE_STREAMLENGTH = 0x1000 /**< 1000000000000 Ignore any stream length information contained in the stream, which can be contained in a 'TLEN' frame of an ID3v2 tag or a Xing tag */
    ,MPG123_SKIP_ID3V2 = 0x2000 /**< 10 0000 0000 0000 Do not parse ID3v2 tags, just skip them. */
};
    
enum  /* mpg123_enc_enum */
{
	 MPG123_ENC_8      = 0x00f  /**<      0000 0000 1111 Some 8 bit  c_integer encoding. */
	,MPG123_ENC_16     = 0x040  /**<      0000 0100 0000 Some 16 bit c_integer encoding. */
	,MPG123_ENC_24     = 0x4000 /**< 0100 0000 0000 0000 Some 24 bit c_integer encoding. */
	,MPG123_ENC_32     = 0x100  /**<      0001 0000 0000 Some 32 bit c_integer encoding. */
	,MPG123_ENC_SIGNED = 0x080  /**<      0000 1000 0000 Some signed c_integer encoding. */
	,MPG123_ENC_FLOAT  = 0xe00  /**<      1110 0000 0000 Some float encoding. */
	,MPG123_ENC_SIGNED_16   = (MPG123_ENC_16|MPG123_ENC_SIGNED|0x10) /**<           1101 0000 signed 16 bit */
	,MPG123_ENC_UNSIGNED_16 = (MPG123_ENC_16|0x20)                   /**<           0110 0000 unsigned 16 bit */
	,MPG123_ENC_UNSIGNED_8  = 0x01                                   /**<           0000 0001 unsigned 8 bit */
	,MPG123_ENC_SIGNED_8    = (MPG123_ENC_SIGNED|0x02)               /**<           1000 0010 signed 8 bit */
	,MPG123_ENC_ULAW_8      = 0x04                                   /**<           0000 0100 ulaw 8 bit */
	,MPG123_ENC_ALAW_8      = 0x08                                   /**<           0000 1000 alaw 8 bit */
	,MPG123_ENC_SIGNED_32   = MPG123_ENC_32|MPG123_ENC_SIGNED|0x1000 /**< 0001 0001 1000 0000 signed 32 bit */
	,MPG123_ENC_UNSIGNED_32 = MPG123_ENC_32|0x2000                   /**< 0010 0001 0000 0000 unsigned 32 bit */
	,MPG123_ENC_SIGNED_24   = MPG123_ENC_24|MPG123_ENC_SIGNED|0x1000 /**< 0101 0000 1000 0000 signed 24 bit */
	,MPG123_ENC_UNSIGNED_24 = MPG123_ENC_24|0x2000                   /**< 0110 0000 0000 0000 unsigned 24 bit */
	,MPG123_ENC_FLOAT_32    = 0x200                                  /**<      0010 0000 0000 32bit float */
	,MPG123_ENC_FLOAT_64    = 0x400                                  /**<      0100 0000 0000 64bit float */
	,MPG123_ENC_ANY = ( MPG123_ENC_SIGNED_16  | MPG123_ENC_UNSIGNED_16 | MPG123_ENC_UNSIGNED_8 
	                  | MPG123_ENC_SIGNED_8   | MPG123_ENC_ULAW_8      | MPG123_ENC_ALAW_8
	                  | MPG123_ENC_SIGNED_32  | MPG123_ENC_UNSIGNED_32
	                  | MPG123_ENC_SIGNED_24  | MPG123_ENC_UNSIGNED_24
	                  | MPG123_ENC_FLOAT_32   | MPG123_ENC_FLOAT_64 ) /**< Any encoding on the list. */
};
enum /* mpg123_channelcount */ { 
    MPG123_MONO = 1, 
    MPG123_STEREO = 2 ,
}

enum /*mpg123_channels */
{
	 MPG123_LEFT=0x1	/**< The Left Channel. */
	,MPG123_RIGHT=0x2	/**< The Right Channel. */
	,MPG123_LR=0x3	/**< Both left and right channel; same as MPG123_LEFT|MPG123_RIGHT */
};
c_int mpg123_init() ;
void mpg123_exit() ; 
c_int mpg123_open(mpg123_handle *mh,  char *path) ;
char** mpg123_decoders() ;
char** mpg123_supported_decoders() ;
mpg123_handle* 	mpg123_new(char* decoder, c_int* error) ;
void mpg123_close(mpg123_handle *mh) ;
void mpg123_delete(mpg123_handle *mh) ;
c_int mpg123_decoder(mpg123_handle* mh, char* decoder_name)  ;
char* mpg123_plain_strerror(c_int errcode) ;
char* mpg123_strerror(mpg123_handle* mh) 	;
c_int  mpg123_errcode(mpg123_handle* mh) 	;
c_int mpg123_param(mpg123_handle *mh,  c_int type, c_long value, double fvalue) ;


void mpg123_rates(/*const*/ c_long **list, size_t *number);
void mpg123_encodings(/*const*/ c_int **list, size_t *number);
c_int mpg123_encsize(c_int encoding);
c_int mpg123_format_none(mpg123_handle *mh);
c_int mpg123_format_all(mpg123_handle *mh);
c_int mpg123_format(mpg123_handle *mh, c_long rate, c_int channels, c_int encodings);
c_int mpg123_format_support(mpg123_handle *mh, c_long rate, c_int encoding);
c_int mpg123_getformat(mpg123_handle *mh, c_long *rate, c_int *channels, c_int *encoding);

size_t mpg123_outblock(mpg123_handle *mh);
size_t mpg123_length(mpg123_handle *mh);
c_off_t mpg123_timeframe(mpg123_handle *mh, c_double sec);
c_off_t mpg123_seek_frame(mpg123_handle *mh, c_off_t frameoff, c_int whence);

/** Read from stream and decode up to outmemsize bytes.
 *  \param outmemory address of output buffer to write to
 *  \param outmemsize maximum number of bytes to write
 *  \param done address to store the number of actually decoded bytes to
 *  \return error/message code (watch out for MPG123_DONE and friends!) */
c_int mpg123_read(mpg123_handle *mh, ubyte *outmemory, size_t outmemsize, size_t *done);

/** Get information about current and remaining frames/seconds.
 *  WARNING: This function is there because of special usage by standalone mpg123 and may be removed in the final version of libmpg123!
 *  You provide an offset (in frames) from now and a number of output bytes 
 *  served by libmpg123 but not yet played. You get the projected current frame 
 *  and seconds, as well as the remaining frames/seconds. This does _not_ care 
 *  about skipped samples due to gapless playback. */
c_int mpg123_position( mpg123_handle *mh, ptrdiff_t frame_offset, ptrdiff_t buffered_bytes, ptrdiff_t *current_frame, ptrdiff_t *frames_left, c_double *current_seconds, c_double *seconds_left);

char[][]  get_mpg123_decoders() {
    static char[][] _decoders ;
    if( _decoders.length is 0 ) {
        auto _ptr   = mpg123_supported_decoders() ;
        while( _ptr !is null ) {
            auto __ptr = _ptr[0] ;
            if( __ptr is null ) break;
            _decoders   ~= __ptr[ 0 .. fromStringz(__ptr).length + 1 ] ;
            //Stdout.formatln("mpg123_supported_decoders= {} ", _decoders[$-1] );
            _ptr++;
        }
        assert(_decoders.length > 0 );
    }
    return _decoders ;
}

